/**
 * Copyright © 2014 TingerSure. All rights reserved.
 */
package org.sure.scomet.link;

import java.io.Serializable;

import javax.servlet.http.HttpSession;

import org.sure.scomet.action.ConnectionAction;
import org.sure.scomet.util.ConfigManager;
import org.sure.scomet.util.UnicodeTool;
import org.sure.scomet.util.log.LogManager;
import org.sure.scomet.util.log.LogType;

/**
 * This is an object which is used to keep connected between Client and Server.
 * 
 * @author TingerSure
 */
public class Connection implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * the buffer length
	 */
	private static int buffer;

	/**
	 * get page id
	 * 
	 * @return page id
	 */
	public int getPageId() {
		return pageId;
	}

	/**
	 * This method is used to handle client message.
	 * 
	 * @param message
	 *            the client message
	 * @param session
	 *            the client
	 * @throws ConnectionOutofMaxTimeException
	 */
	public void clientMessage(String message, HttpSession session)
			throws ConnectionOutofMaxTimeException {
		ConnectionCenter.check(this);
		message = formatText(message);
		message = UnicodeTool.unicodeToLocalCharacter(message);
		try {
			ConnectionCenter.getCallBack().message(pageId, session, message);
		} catch (NullPointerException e) {
			LogManager.logException("no call back method", e, LogType.warn);
		}
	}

	/**
	 * format the client message.<br>
	 * // -> / <br>
	 * /s -> # <br>
	 * /a -> & <br>
	 * /p -> + <br>
	 * 
	 * @param message
	 * @return
	 */
	private String formatText(String message) {
		StringBuffer buf = new StringBuffer(message);
		for (int i = 0; i < buf.length() - 1; i++) {
			if (buf.charAt(i) == '/') {
				switch (buf.charAt(i + 1)) {
				case '/':
					buf.replace(i, i + 2, "/");
					break;
				case 's':
					buf.replace(i, i + 2, "#");
					break;
				case 'a':
					buf.replace(i, i + 2, "&");
					break;
				case 'p':
					buf.replace(i, i + 2, "+");
					break;
				}
			}
		}
		return buf.toString();
	}

	/**
	 * the Server message buffer
	 */
	private StringBuffer backMessage = new StringBuffer();

	/**
	 * Send a message to the Client. In most time, the message is in the buffer.
	 * You should call the method flush() to make sure the message has been sent
	 * already.
	 * 
	 * @param message
	 * @return be sent already.
	 * @throws ConnectionOutofMaxTimeException
	 *             when connection is closed.
	 */
	public boolean write(String message) throws ConnectionOutofMaxTimeException {
		ConnectionCenter.check(this);
		lastLink = System.currentTimeMillis();
		backMessage.append(message);
		if (backMessage.length() > buffer) {
			this.flush();
			return true;
		}
		return false;
	}

	/**
	 * flush the buffer.
	 * 
	 * @throws ConnectionOutofMaxTimeException
	 *             when connection is closed.
	 */
	public void flush() throws ConnectionOutofMaxTimeException {
		ConnectionCenter.check(this);
		if (backMessage.length() == 0) {
			return;
		}
		for (int i = 0; i < 60; i++) {
			if (send == null) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					LogManager.logException("buffer flush wrong", e,
							LogType.error);
				}
				continue;
			}
			send.setMessage(backMessage.toString());
			synchronized (send) {
				send.notify();
			}
			send = null;
			backMessage.delete(0, backMessage.length());
			lastLink = System.currentTimeMillis();
			return;
		}
		close();
		throw new ConnectionOutofMaxTimeException("no connection here.");
	}

	/**
	 * set the page id.
	 * 
	 * @param pageId
	 *            the page id
	 */
	protected void setPageId(int pageId) {
		this.pageId = pageId;
	}

	/**
	 * set the connection action
	 * 
	 * @param send
	 *            the connection action
	 */

	public void setSend(ConnectionAction send) {
		this.send = send;
	}

	/**
	 * get the last link time.
	 * 
	 * @return the last link time
	 */
	public long getLastLink() {
		return lastLink;
	}

	/**
	 * the last link time
	 */
	private long lastLink = System.currentTimeMillis();

	/**
	 * the page id
	 */
	private int pageId;

	/**
	 * the connection action which is waiting for the message
	 */

	private ConnectionAction send = null;

	/**
	 * get the connection action.
	 * 
	 * @return the connection action
	 */
	public ConnectionAction getSend() {
		return send;
	}

	/**
	 * Initialize all attribute in need.
	 */
	public static void init() {
		try {
			buffer = Integer.parseInt(ConfigManager.getConfig(
					"connection-buffer", "1000"));
			if (buffer < 1) {
				throw new NumberFormatException(Integer.toString(buffer));
			}
		} catch (NumberFormatException e) {
			LogManager
					.logException(
							"The config length of the connection string buffer is wrong! ",
							e, LogType.fatal);
		}
	}

	/**
	 * Close this connection.
	 */
	public void close() {
		ConnectionCenter.closeConnection(pageId);
	}

	/**
	 * Destroy all attribute in need.
	 */
	public static void destroy() {
		buffer = 0;
	}
}
